#pragma once
#include "Requestor.hpp"
#include <unordered_set>
namespace bitrpc
{
    namespace client
    {
        class Provider
        {
        public:
            Provider(const Requestor::ptr &requestor) : _requestor(requestor)
            {
            }

            using ptr = std::shared_ptr<Provider>;
            bool registryMethod( const BaseConnection ::ptr& conn, const std::string &method, const Address &host)
            {
                auto req_msg = MessageFactory::create<ServiceRequest>();
                req_msg->SetId(UUID::uuid());
                req_msg->SetMessageType(MessageType::REQ_SERVICE);
                req_msg->setMethod(method);
                req_msg->setServiceOptype(ServiceOptype::SERVICE_REGISTRY);
                req_msg->setHost(host);
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->send(conn, req_msg, msg_rsp);
                if (ret == false)
                {
                    ELOG("%s 服务注册失败！", method.c_str());
                    return false;
                }
                auto rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (rsp.get() == nullptr)
                {
                    ELOG("响应类型向下转换失败");
                    return false;
                }
                if (rsp->rcode() != RCODE::RCODE_OK)
                {
                    ELOG("服务注册失败，失败原因：%s", errReason(rsp->rcode()).c_str());
                    return false;
                }
                return true;
            }

        private:
            Requestor::ptr _requestor;
        };

        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost() : _idx(0)
            {
            }
            MethodHost(const std::vector<Address> &host) : _hosts(host.begin(), host.end()), _idx(0)
            {
            }

            void addpendHost(const Address &host)
            {
                // 中途收到了服务上线请求后被调用
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }
            void RemoveHost(const Address &host)
            {
                // 中途收到了服务下线请求后被调用
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if (*it == host)
                    {
                        _hosts.erase(it);
                        return;
                    }
                }
            }
            // 负载均衡：采用轮询的方式调用同一服务的所有主机
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto pos = _idx++ % _hosts.size();
                return _hosts[pos];
            }
            bool Empty() // 如果是空的，就向注册中心发送服务发现请求
            {
                std::unique_lock<std::mutex> lock(_mutex);

                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            std::vector<Address> _hosts;
            int _idx;
        };
        class Discover
        {
        public:
            using OfflinieCallback = function<void(const Address& host)>;//服务下线时的对连接池的处理回调函数
            using ptr = std::shared_ptr<Discover>;

            Discover(const Requestor::ptr &requestor,OfflinieCallback cb) : _requestor(requestor),_off_callback(cb)
            {
            }
            // 服务发现功能
            bool serviceDiscover(const BaseConnection::ptr &conn, const std::string &method, Address &host)
            {
                
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    // 先检查有没有该服务对应的一系列提供者，如果有，就按照负载策略选择一个提供者的主机信息
                    auto it = _method_hosts.find(method);
                    if (it != _method_hosts.end())
                    {
                        if (it->second->Empty() == false)
                        {
                            host = it->second->chooseHost();
                        }
                    }
                }

                // 该服务没有提供者，需要进行服务发现请求
                auto req = MessageFactory::create<ServiceRequest>();
                req->SetId(UUID::uuid());
                req->SetMessageType(MessageType::REQ_SERVICE);
                req->setServiceOptype(ServiceOptype::SERVICE_DISCOVERY);
                req->setMethod(method);
                BaseMessage::ptr rsp;
                bool ret = _requestor->send(conn, req, rsp);
                if (ret == false)
                {
                    ELOG("服务发现请求发送失败");
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(rsp);
                if (!service_rsp)
                {
                    ELOG("类型向下转换失败");
                    return false;
                }
                if (service_rsp->rcode() != RCODE::RCODE_OK)
                {
                    ELOG("服务发现失败，原因：%s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                // 当前是没有服务对应的提供主机的
                std::unique_lock<std::mutex> lock(_mutex);
                auto method_host = std::make_shared<MethodHost>(service_rsp->hosts());
                if (method_host->Empty())
                {
                    ELOG("服务发现失败,没有能提供服务的主机");
                    return false;
                }
                _method_hosts[method] = method_host;
                host = method_host->chooseHost();
                return true;
            }
            // 这个接口是提供给dispatcher模块进行服务上线下线请求处理的回调函数
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                // 判断是上线还是下线请求，如果都不是，就不处理
                auto optype = msg->serviceOptype();
                string method = msg->method();
                std::unique_lock<std::mutex> lock(_mutex);

                if (optype == ServiceOptype::SERVICE_ONLINE)
                {
                    // 上线请求：找到methodHost,向其中新增一个主机地址
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        auto method_host = std::make_shared<MethodHost>();
                        method_host->addpendHost(msg->host());
                        _method_hosts[method] = method_host;
                    }
                    else
                    {
                        it->second->addpendHost(msg->host());
                    }
                }
                else if (optype == ServiceOptype::SERVICE_OFFLINE)
                {
                    // 下线请求：找到methodHost,从其中删除一个主机地址
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        return;
                    }
                    it->second->RemoveHost(msg->host());
                    //从连接池中删除一个主机
                    _off_callback(msg->host());
                }
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, MethodHost::ptr> _method_hosts; // 一个方法能由哪些服务提供者提供
            Requestor::ptr _requestor;
            OfflinieCallback _off_callback;
        };
    }
}